﻿//
// CharacterPhysicsComponent.cs
//
// This file is part of the QuickStart Engine. See http://www.codeplex.com/QuickStartEngine
// for license details.
//

using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Alex_s_Dream_The_Toy_World
{
    public class CharacterPhysicsComponent : PhysicsComponent
    {
        protected bool MoveBasedOnCameraOrientation = false;

        public CharacterPhysicsComponent(BaseEntity parent, ShapeType type, float density)
            : base(parent, type, density, true)
        {
            ActivateComponent();
        }

        /// <summary>
        /// Creates an actor using shape info and information from the parent BaseEntity.
        /// </summary>
        /// <param name="PhysicsScene">Reference to the physics scene</param>
        protected override void CreateActor(IPhysicsScene PhysicsScene)
        {
            ShapeDesc newShape = CreateShapeFromType(shapeType);

            // Phantoms cannot currently have dynamic physics
            this.isDynamic = false;

            ActorDesc desc = new ActorDesc();
            desc.Orientation = parentEntity.Rotation;
            desc.Density = this.density;
            desc.Dynamic = true;
            desc.Position = this.parentEntity.Position;
            desc.Shapes.Add(newShape);
            desc.EntityID = this.parentEntity.UniqueID;
            desc.Game = this.parentEntity.Game;
            desc.Phantom = false;
            desc.Character = true;

            this.actor = PhysicsScene.CreateActor(desc);

            if (this.actor != null)
            {
                this.actor.EnableCollisionListening();
            }
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            MsgGetCharacterMovement msgGetMovement = ObjectPool.Aquire<MsgGetCharacterMovement>();            
            msgGetMovement.UniqueTarget = this.parentEntity.UniqueID;
            this.parentEntity.Game.SendMessage(msgGetMovement);
            
            ProcessMovementFromInput( msgGetMovement.Data ); 
        }

        /// <summary>
        /// Handles a message sent to this component.
        /// </summary>
        /// <param name="message">Message to be handled</param>
        /// <returns>True, if handled, otherwise false</returns>
        public override bool ExecuteMessage(IMessage message)
        {
            if (message.UniqueTarget != this.parentEntity.UniqueID)
                return false;

            switch (message.Type)
            {           
                case MessageType.SetRotation:
                    {
                        MsgSetRotation setRotMsg = message as MsgSetRotation;
                        message.TypeCheck(setRotMsg);

                        SetRotation(setRotMsg.Rotation);
                    }
                    return true;
                case MessageType.ModifyRotation:
                    {
                        MsgModifyRotation modRotMsg = message as MsgModifyRotation;
                        message.TypeCheck(modRotMsg);

                        SetRotation(this.actor.Orientation *= modRotMsg.Rotation);
                    }
                    return true;
                case MessageType.LockCharRotationToCamera:
                    {
                        MsgLockCharacterRotationToCamera orientMsg = message as MsgLockCharacterRotationToCamera;
                        message.TypeCheck(orientMsg);

                        MoveBasedOnCameraOrientation = orientMsg.LockRotation;
                    }
                    return true;
                case MessageType.GetIsACharacter:
                    {
                        MsgGetIsACharacter msgIsChar = message as MsgGetIsACharacter;
                        message.TypeCheck(msgIsChar);

                        msgIsChar.IsCharacter = true;
                    }
                    return true;
                default:
                    return base.ExecuteMessage(message);
            }
        }

        protected void ProcessMovementFromInput(CharacterMovementInfo movementData)
        {
            Vector3 linForce = Vector3.Zero;
            Vector3 forwardForMovement;
            Vector3 rightForMovement;

            if (MoveBasedOnCameraOrientation)
            {
                // Grab the current camera's ID
                MsgGetRenderEntity camMsg = ObjectPool.Aquire<MsgGetRenderEntity>();
                this.parentEntity.Game.SendInterfaceMessage(camMsg, InterfaceType.Camera);

                // Get the current camera's rotation
                MsgGetRotation msgGetRot = ObjectPool.Aquire<MsgGetRotation>();
                msgGetRot.UniqueTarget = camMsg.EntityID;
                this.parentEntity.Game.SendMessage(msgGetRot);

                // We set the character's orientation to match the camera's (except along the Y axis)
                Matrix newRotMat = msgGetRot.Rotation;
                forwardForMovement = newRotMat.Forward;
                forwardForMovement.Y = 0;
                forwardForMovement.Normalize();

                rightForMovement = Vector3.Cross(forwardForMovement, Vector3.Up);
                
                newRotMat.Forward = forwardForMovement;
                newRotMat.Right = rightForMovement;
                newRotMat.Up = Vector3.Up;

                MsgSetRotation msgSetRot = ObjectPool.Aquire<MsgSetRotation>();
                msgSetRot.Rotation = newRotMat;
                msgSetRot.UniqueTarget = this.parentEntity.UniqueID;
                this.parentEntity.Game.SendMessage(msgSetRot);
            }
            else
            {
                forwardForMovement = this.parentEntity.Rotation.Forward;
                rightForMovement = this.parentEntity.Rotation.Right;
                
                if (movementData.clockwise != 0.0f)
                {
                    Matrix rotMat = Matrix.CreateRotationY(movementData.clockwise);

                    MsgModifyRotation msgModRot = ObjectPool.Aquire<MsgModifyRotation>();
                    msgModRot.Rotation = rotMat;
                    msgModRot.UniqueTarget = this.parentEntity.UniqueID;
                    this.parentEntity.Game.SendMessage(msgModRot);
                }
            }

            if (movementData.forward != 0.0f)
            {
                linForce += (forwardForMovement * movementData.forward);
            }

            if (movementData.right != 0.0f)
            {
                linForce += (rightForMovement * movementData.right);
            }

            MsgAddLinearForce msgAddForce = ObjectPool.Aquire<MsgAddLinearForce>();
            msgAddForce.LinearVelocity = linForce;
            msgAddForce.UniqueTarget = this.parentEntity.UniqueID;
            this.parentEntity.Game.SendMessage(msgAddForce);

            if (movementData.wantsJump)
            {
                this.actor.CharacterDoJump();
            }
        }
    }
}
